package com.shiro.demo.config;

import com.shiro.demo.domain.Pager;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
 * @author tracyclock 2017-12-08
 * 分页插件
 */
@Intercepts({@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class,Integer.class}
)})
public class PagePlugin implements Interceptor {
    public PagePlugin() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        if (target instanceof StatementHandler) {
            StatementHandler statementHandler = (StatementHandler)target;
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            RowBounds rowBounds = null;

            try {
                rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
                if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
                    return invocation.proceed();
                }
            } catch (Exception var11) {
                invocation.getTarget();
            }

            BoundSql boundSql = (BoundSql)metaStatementHandler.getValue("delegate.boundSql");
            String originalSql = boundSql.getSql();
            if (rowBounds instanceof Pager) {
                MappedStatement mappedStatement = (MappedStatement)metaStatementHandler.getValue("delegate.mappedStatement");
                Connection connection = (Connection)invocation.getArgs()[0];
                Pager page = (Pager)rowBounds;
                page = this.count(originalSql, connection, mappedStatement, boundSql, page);
                if (page.getTotal() <= 0L) {
                    return invocation.proceed();
                }

                originalSql = this.buildPageSql(originalSql, page.getPageNumber(), page.getSize());
            }

            metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
        }

        return invocation.proceed();
    }

    private String buildPageSql(String originalSql, int offset, int limit) {
        StringBuilder sql = new StringBuilder(originalSql);
        sql.append(" LIMIT ").append(offset).append(",").append(limit);
        return sql.toString();
    }

    private Pager count(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Pager page) {
        String sqlUse = sql;
        int order_by = sql.toUpperCase().lastIndexOf("ORDER BY");
        if (order_by > -1) {
            sqlUse = sql.substring(0, order_by);
        }

        StringBuffer countSql = new StringBuffer();
        countSql.append("SELECT COUNT(1) AS TOTAL FROM (").append(sqlUse).append(") A");
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            pstmt = connection.prepareStatement(countSql.toString());
            BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql.toString(), boundSql.getParameterMappings(), boundSql.getParameterObject());
            ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), countBS);
            parameterHandler.setParameters(pstmt);
            rs = pstmt.executeQuery();
            int total = 0;
            if (rs.next()) {
                total = rs.getInt(1);
            }

            page.setTotal((long)total);
        } catch (SQLException var22) {
            var22.printStackTrace();
        } finally {
            try {
                rs.close();
                pstmt.close();
            } catch (SQLException var21) {
                var21.printStackTrace();
            }

        }

        return page;
    }

    public Object plugin(Object target) {
        return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {

    }

}
